﻿using System;

namespace TI_BLE_HCI_ClientLib.Controllers.GATT_Entities
{
    public class GATT_Characteristic
    {
        ushort _configurationAttributeHandle;
        ushort _declarationAttributeHandle;
        ushort _userDescriptionAttributeHandle;
        ushort _presentationFormatAttributeHandle;

        string _identifier;
        bool _notificationEnabled = false;
        bool _indicationEnabled = false;
        PermissionsEnum _permissions = PermissionsEnum.Read;
        ushort _type;
        ushort _valueAttributeHandle;
        ValueTypeEnum _valueType = ValueTypeEnum.ByteArray;
        string _userDescription;
        CharacteristicPresentationFormatDescriptor _presentationFormat;

        internal GATT_Characteristic(string identifier, PermissionsEnum permissions, ValueTypeEnum valueType)
        {
            _identifier = identifier;
            _permissions = permissions;
            _valueType = valueType;
            _userDescription = _identifier;

            _presentationFormat = new CharacteristicPresentationFormatDescriptor()
            {
                Format = mapValueTypeToFormat(valueType),
                Exponent = 1,
                Unit = CharacteristicPresentationFormatDescriptor.CharacteristicUnitEnum.unitless,
                NameSpace = CharacteristicPresentationFormatDescriptor.CharacteristicNameSpaceEnum.None,
                NameSpaceDescription = CharacteristicPresentationFormatDescriptor.CharacteristicNameSpaceDescriptionEnum.unknown,
            };
        }

        internal GATT_Characteristic(string identifier, PermissionsEnum permissions, ValueTypeEnum valueType, string userDescription)
            : this(identifier, permissions, valueType)
        {
            _userDescription = userDescription;
        }

        [Flags]
        public enum PermissionsEnum
        {
            None = 0,
            Read = 1,
            Write = 2,
            Notify = 4,
            Indicate = 8
        }

        public enum ValueTypeEnum
        {
            ByteArray,
            Int32,
            UInt32,
            Int16,
            UInt16,
            Int8,
            UInt8,
            Boolean,
            UTF8String,
            Float,
            Double
        }

        [Flags]
        public enum CharacteristicConfigurationFlagsEnum : byte
        {
            notification_enabled = 0x01,
            indication_enabled = 0x02
        }

        public CharacteristicPresentationFormatDescriptor PresentationFormat
        {
            get { return _presentationFormat; }
            set { _presentationFormat = value; }
        }

        public string UserDescription
        {
            get { return _userDescription; }
            set { _userDescription = value; }
        }

        public ValueTypeEnum ValueType
        {
            get { return _valueType; }
            internal set { _valueType = value; }
        }

        public ushort ConfigurationAttributeHandle
        {
            get { return _configurationAttributeHandle; }
            internal set { _configurationAttributeHandle = value; }
        }

        public ushort UserDescriptionAttributeHandle
        {
            get { return _userDescriptionAttributeHandle; }
            internal set { _userDescriptionAttributeHandle = value; }
        }

        public ushort PresentationFormatAttributeHandle
        {
            get { return _presentationFormatAttributeHandle; }
            internal set { _presentationFormatAttributeHandle = value; }
        }

        public ushort DeclarationAttributeHandle
        {
            get { return _declarationAttributeHandle; }
            internal set { _declarationAttributeHandle = value; }
        }

        public string Identifier
        {
            get { return _identifier; }
        }

        public bool NotificationEnabled
        {
            get { return _notificationEnabled; }
            internal set { _notificationEnabled = value; }
        }

        public bool IndicationEnabled
        {
            get { return _indicationEnabled; }
            internal set { _indicationEnabled = value; }
        }

        public PermissionsEnum Permissions
        {
            get { return _permissions; }
            internal set { _permissions = value; }
        }

        public ushort Type
        {
            get { return _type; }
            internal set { _type = value; }
        }

        public ushort ValueAttributeHandle
        {
            get { return _valueAttributeHandle; }
            internal set { _valueAttributeHandle = value; }
        }

        static CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum mapValueTypeToFormat(ValueTypeEnum valueType)
        {
            switch (valueType)
            {
                case ValueTypeEnum.Boolean:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.boolean;

                case ValueTypeEnum.ByteArray:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.structure;

                case ValueTypeEnum.Double:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.float64;

                case ValueTypeEnum.Float:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.float32;

                case ValueTypeEnum.Int16:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.sint16;

                case ValueTypeEnum.Int32:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.sint32;

                case ValueTypeEnum.Int8:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.sint8;

                case ValueTypeEnum.UInt16:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.uint16;

                case ValueTypeEnum.UInt32:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.uint32;

                case ValueTypeEnum.UInt8:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.uint8;

                case ValueTypeEnum.UTF8String:
                    return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.utf8s;
            }

            return CharacteristicPresentationFormatDescriptor.CharacteristicFormatEnum.rfu;
        }

        public class CharacteristicPresentationFormatDescriptor
        {
            public enum CharacteristicFormatEnum : byte
            {
                rfu = 0x00,
                boolean = 0x01,
                twobitsinteger = 0x02,
                nibble = 0x03,
                uint8 = 0x04,
                uint12 = 0x05,
                uint16 = 0x06,
                uint24 = 0x07,
                uint32 = 0x08,
                uint48 = 0x09,
                uint64 = 0x0A,
                uint128 = 0x0B,
                sint8 = 0x0C,
                sint12 = 0x0D,
                sint16 = 0x0E,
                sint24 = 0x0F,
                sint32 = 0x10,
                sint48 = 0x11,
                sint64 = 0x12,
                sint128 = 0x13,
                float32 = 0x14,
                float64 = 0x15,
                sfloat = 0x16,
                dfloat = 0x17,
                duint16 = 0x18,
                utf8s = 0x19,
                utf16s = 0x1a,
                structure = 0x1b
            }

            public enum CharacteristicUnitEnum : ushort
            {
                unitless = 0x2700,
                length_metre_ = 0x2701,
                mass_kilogram_ = 0x2702,
                time_second_ = 0x2703,
                electric_current_ampere_ = 0x2704,
                thermodynamic_temperature_kelvin_ = 0x2705,
                amount_of_substance_mole_ = 0x2706,
                luminous_intensity_candela_ = 0x2707,
                area_square_metres_ = 0x2710,
                volume_cubic_metres_ = 0x2711,
                velocity_metres_per_second_ = 0x2712,
                acceleration_metres_per_second_squared_ = 0x2713,
                wavenumber_reciprocal_metre_ = 0x2714,
                density_kilogram_per_cubic_metre_ = 0x2715,
                surface_density_kilogram_per_square_metre_ = 0x2716,
                specific_volume_cubic_metre_per_kilogram_ = 0x2717,
                current_density_ampere_per_square_metre_ = 0x2718,
                magnetic_field_strength_ampere_per_metre_ = 0x2719,
                amount_concentration_mole_per_cubic_metre_ = 0x271A,
                mass_concentration_kilogram_per_cubic_metre_ = 0x271B,
                luminance_candela_per_square_metre_ = 0x271C,
                refractive_index = 0x271D,
                relative_permeability = 0x271E,
                plane_angle_radian_ = 0x2720,
                solid_angle_steradian_ = 0x2721,
                frequency_hertz_ = 0x2722,
                force_newton_ = 0x2723,
                pressure_pascal_ = 0x2724,
                energy_joule_ = 0x2725,
                power_watt_ = 0x2726,
                electric_charge_coulomb_ = 0x2727,
                electric_potential_difference_volt_ = 0x2728,
                capacitance_farad_ = 0x2729,
                electric_resistance_ohm_ = 0x272A,
                electric_conductance_siemens_ = 0x272B,
                magnetic_flux_weber_ = 0x272C,
                magnetic_flux_density_tesla_ = 0x272D,
                inductance_henry_ = 0x272E,
                Celsius_temperature_degree_Celsius_ = 0x272F,
                luminous_flux_lumen_ = 0x2730,
                illuminance_lux_ = 0x2731,
                activity_referred_to_a_radionuclide_becquerel_ = 0x2732,
                absorbed_dose_gray_ = 0x2733,
                dose_equivalent_sievert_ = 0x2734,
                catalytic_activity_katal_ = 0x2735,
                dynamic_viscosity_pascal_second_ = 0x2740,
                moment_of_force_newton_metre_ = 0x2741,
                surface_tension_newton_per_metre_ = 0x2742,
                angular_velocity_radian_per_second_ = 0x2743,
                angular_acceleration_radian_per_second_squared_ = 0x2744,
                heat_flux_density_watt_per_square_metre_ = 0x2745,
                heat_capacity_joule_per_kelvin_ = 0x2746,
                specific_heat_capacity_joule_per_kilogram_kelvin_ = 0x2747,
                specific_energy_joule_per_kilogram_ = 0x2748,
                thermal_conductivity_watt_per_metre_kelvin_ = 0x2749,
                energy_density_joule_per_cubic_metre_ = 0x274A,
                electric_field_strength_volt_per_metre_ = 0x274B,
                electric_charge_density_coulomb_per_cubic_metre_ = 0x274C,
                surface_charge_density_coulomb_per_square_metre_ = 0x274D,
                electric_flux_density_coulomb_per_square_metre_ = 0x274E,
                permittivity_farad_per_metre_ = 0x274F,
                permeability_henry_per_metre_ = 0x2750,
                molar_energy_joule_per_mole_ = 0x2751,
                molar_entropy_joule_per_mole_kelvin_ = 0x2752,
                exposure_coulomb_per_kilogram_ = 0x2753,
                absorbed_dose_rate_gray_per_second_ = 0x2754,
                radiant_intensity_watt_per_steradian_ = 0x2755,
                radiance_watt_per_square_metre_steradian_ = 0x2756,
                catalytic_activity_concentration_katal_per_cubic_metre_ = 0x2757,
                time_minute_ = 0x2760,
                time_hour_ = 0x2761,
                time_day_ = 0x2762,
                plane_angle_degree_ = 0x2763,
                plane_angle_minute_ = 0x2764,
                plane_angle_second_ = 0x2765,
                area_hectare_ = 0x2766,
                volume_litre_ = 0x2767,
                mass_tonne_ = 0x2768,
                pressure_bar_ = 0x2780,
                pressure_millimetre_of_mercury_ = 0x2781,
                length_ångström_ = 0x2782,
                length_nautical_mile_ = 0x2783,
                area_barn_ = 0x2784,
                velocity_knot_ = 0x2785,
                logarithmic_radio_quantity_neper_ = 0x2786,
                logarithmic_radio_quantity_bel_ = 0x2787,
                length_yard_ = 0x27A0,
                length_parsec_ = 0x27A1,
                length_inch_ = 0x27A2,
                length_foot_ = 0x27A3,
                length_mile_ = 0x27A4,
                pressure_pound_force_per_square_inch_ = 0x27A5,
                velocity_kilometre_per_hour_ = 0x27A6,
                velocity_mile_per_hour_ = 0x27A7,
                angular_velocity_revolution_per_minute_ = 0x27A8,
                energy_gram_calorie_ = 0x27A9,
                energy_kilogram_calorie_ = 0x27AA,
                energy_kilowatt_hour_ = 0x27AB,
                thermodynamic_temperature_degree_Fahrenheit_ = 0x27AC,
                percentage = 0x27AD,
                per_mille = 0x27AE,
                period_beats_per_minute_ = 0x27AF,
                electric_charge_ampere_hours_ = 0x27B0,
                mass_density_milligram_per_decilitre_ = 0x27B1,
                mass_density_millimole_per_litre_ = 0x27B2,
                time_year_ = 0x27B3,
                time_month_ = 0x27B4,
                concentration_count_per_cubic_metre_ = 0x27B5,
                irradiance_watt_per_square_metre_ = 0x27B6,
                milliliter_per_kilogram_per_minute_ = 0x27B7,
                mass_pound_ = 0x27B8
            }

            public enum CharacteristicNameSpaceEnum : byte
            {
                None = 0x00,
                BluetoothSIG = 0x01
            }

            public enum CharacteristicNameSpaceDescriptionEnum : ushort
            {
                unknown=0x0000,
                front = 0x0100,
                back = 0x0101,
                top = 0x0102,
                bottom = 0x0103,
                upper = 0x0104,
                lower = 0x0105,
                main = 0x0106,
                backup = 0x0107,
                auxiliary = 0x0108,
                supplementary = 0x0109,
                flash = 0x010A,
                inside = 0x010B,
                outside = 0x010C,
                left = 0x010D,
                right = 0x010E,
                _internal = 0x010F,
                external = 0x0110
            }

            public CharacteristicFormatEnum Format;
            public sbyte Exponent;
            public CharacteristicUnitEnum Unit;
            public CharacteristicNameSpaceEnum NameSpace;
            public CharacteristicNameSpaceDescriptionEnum NameSpaceDescription;

            public byte[] ToByteArray()
            {
                var buf = new byte[7];

                buf[0] = (byte)this.Format;
                buf[1] = (byte)this.Exponent;
                buf[2] = (byte)((ushort)this.Unit & 0xFF);
                buf[3] = (byte)(((ushort)this.Unit >> 8) & 0xFF);
                buf[4] = (byte)this.NameSpace;
                buf[5] = (byte)((ushort)this.NameSpaceDescription & 0xFF);
                buf[6] = (byte)(((ushort)this.NameSpaceDescription >> 8) & 0xFF);

                return buf;
            }
        }
    }
}
